/*
 * Copyright (c) 2021-2023, Wei.Studio
 *
 * License: Apache-2.0
 *
 * Change Logs:
 * Date           Author        Notes
 * 2021-07-13     Wei.Studio    the first version
 */

#include "qe_core.h"
#include "bget.h"


enum qemem_type {
	QEMEM_NONE = 0,
	QEMEM_EAPI,
	QEMEM_HEAP,
};

struct qemem_context {
	
	enum qemem_type type;
	
	qe_size_t total_bytes;
	qe_size_t alloced_bytes;

	void *heap_start;
	qe_size_t heap_size;

	struct {
		void* (*malloc)(qe_size_t size);
		void* (*zalloc)(qe_size_t size);
		void* (*realloc)(void *buf, qe_size_t size);
		void  (*free)(void *ptr);
	}eapi;
};

static struct qemem_context memctx = {
	.type          = QEMEM_NONE,
	.alloced_bytes = 0,
	.total_bytes   = 0,
	.heap_start    = QE_NULL,
	.heap_size     = 0,
	.eapi = {
		.malloc    = QE_NULL,
		.zalloc    = QE_NULL,
		.realloc   = QE_NULL,
		.free      = QE_NULL,
	}
};

#ifndef CONFIG_PLATFORM_OS
void *qe_malloc(qe_size_t size)
{
	void *buf = QE_NULL;

	if (memctx.type == QEMEM_EAPI) {
		if (memctx.eapi.malloc != QE_NULL) {
			buf = memctx.eapi.malloc(size);
		}
	} else if (memctx.type == QEMEM_HEAP) {
		buf = bget(size);
	}

	return buf;
}

void *qe_zalloc(qe_size_t size)
{
	void *buf;

	if (memctx.type == QEMEM_EAPI) {
		if (memctx.eapi.zalloc != QE_NULL) {
			buf = memctx.eapi.zalloc(size);
		}
	} else if (memctx.type == QEMEM_HEAP) {
		buf = bgetz(size);
	}

	return buf;
}

void *qe_realloc(void *buf, qe_size_t size)
{
	void *nbuf;
	qe_assert(buf != QE_NULL);
	
	if (memctx.type == QEMEM_EAPI) {
		if (memctx.eapi.zalloc != QE_NULL) {
			nbuf = memctx.eapi.realloc(buf, size);
		}
		return QE_NULL;
	} else if (memctx.type == QEMEM_HEAP) {
		nbuf = bgetr(buf, size);
	}

	return nbuf;
}

void qe_free(void *ptr)
{
	if (memctx.type == QEMEM_EAPI) {
		if (memctx.eapi.free != QE_NULL) {
			memctx.eapi.free(ptr);
		}
	} else if (memctx.type == QEMEM_HEAP) {
		brel(ptr);
	}
}
#endif

void qemem_heap_set(void *start, qe_size_t size)
{
	static qe_bool_t initialized = qe_false;

	if (!initialized) {
		bpool(start, size);
		memctx.heap_start = start;
		memctx.heap_size  = size;
		memctx.type = QEMEM_HEAP;
		initialized = qe_true;
	}
}

void qemem_eapi_set(void* (*malloc)(qe_size_t size),
					void* (*zalloc)(qe_size_t size),
					void* (*realloc)(void *buf, qe_size_t size),
					void  (*free)(void *ptr))
{
	static qe_bool_t initialized = qe_false;

	if (!malloc || !free)
		return;

	if (!initialized) {
		memctx.eapi.free    = free;
		memctx.eapi.malloc  = malloc;
		memctx.eapi.zalloc  = zalloc;
		memctx.eapi.realloc = realloc;
		memctx.type         = QEMEM_EAPI;
		initialized = qe_true;
	}
}

qe_size_t qe_weak qemem_heap_get_freesize(void)
{
	if (memctx.type == QEMEM_HEAP) {
		long alloc, totfree, maxfree, nget, nrel;
		bstats(&alloc, &totfree, &maxfree, &nget, &nrel);
		return totfree;
	}
	return 0;
}